home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2008 February / PCWFEB08.iso / Software / Resources / Developers / XAMPP 1.5.4 / Windows installer / xampp-win32-1.5.4-installer.exe / xampp / php / pear / Net / POP3.php < prev    next >
Encoding:
PHP Script  |  2005-07-07  |  32.9 KB  |  1,227 lines

  1. <?php
  2. // +-----------------------------------------------------------------------+
  3. // | Copyright (c) 2002, Richard Heyes                                     |
  4. // | All rights reserved.                                                  |
  5. // |                                                                       |
  6. // | Redistribution and use in source and binary forms, with or without    |
  7. // | modification, are permitted provided that the following conditions    |
  8. // | are met:                                                              |
  9. // |                                                                       |
  10. // | o Redistributions of source code must retain the above copyright      |
  11. // |   notice, this list of conditions and the following disclaimer.       |
  12. // | o Redistributions in binary form must reproduce the above copyright   |
  13. // |   notice, this list of conditions and the following disclaimer in the |
  14. // |   documentation and/or other materials provided with the distribution.|
  15. // | o The names of the authors may not be used to endorse or promote      |
  16. // |   products derived from this software without specific prior written  |
  17. // |   permission.                                                         |
  18. // |                                                                       |
  19. // | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS   |
  20. // | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT     |
  21. // | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
  22. // | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT  |
  23. // | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
  24. // | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT      |
  25. // | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
  26. // | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
  27. // | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT   |
  28. // | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
  29. // | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  |
  30. // |                                                                       |
  31. // +-----------------------------------------------------------------------+
  32. // | Author: Richard Heyes <richard@phpguru.org>                           |
  33. // | Co-Author: Damian Fernandez Sosa <damlists@cnba.uba.ar>               |
  34. // +-----------------------------------------------------------------------+
  35. //
  36. // $Id: POP3.php,v 1.2 2004/12/05 16:34:39 damian Exp $
  37.  
  38. require_once('Net/Socket.php');
  39.  
  40.  
  41.  
  42. /**
  43. *  +----------------------------- IMPORTANT ------------------------------+
  44. *  | Usage of this class compared to native php extensions such as IMAP   |
  45. *  | is slow and may be feature deficient. If available you are STRONGLY  |
  46. *  | recommended to use the php extensions.                               |
  47. *  +----------------------------------------------------------------------+
  48. *
  49. * POP3 Access Class
  50. *
  51. * For usage see the example script
  52. */
  53.  
  54. define('NET_POP3_STATE_DISCONNECTED',  1, true);
  55. define('NET_POP3_STATE_AUTHORISATION', 2, true);
  56. define('NET_POP3_STATE_TRANSACTION',   4, true);
  57.  
  58. class Net_POP3 {
  59.  
  60.     /*
  61.     * Some basic information about the mail drop
  62.     * garnered from the STAT command
  63.     *
  64.     * @var array
  65.     */
  66.     var $_maildrop;
  67.  
  68.     /*
  69.     * Used for APOP to store the timestamp
  70.     *
  71.     * @var string
  72.     */
  73.     var $_timestamp;
  74.  
  75.     /*
  76.     * Timeout that is passed to the socket object
  77.     *
  78.     * @var integer
  79.     */
  80.     var $_timeout;
  81.  
  82.     /*
  83.     * Socket object
  84.     *
  85.     * @var object
  86.     */
  87.     var $_socket;
  88.  
  89.     /*
  90.     * Current state of the connection. Used with the
  91.     * constants defined above.
  92.     *
  93.     * @var integer
  94.     */
  95.     var $_state;
  96.  
  97.     /*
  98.     * Hostname to connect to
  99.     *
  100.     * @var string
  101.     */
  102.     var $_host;
  103.  
  104.     /*
  105.     * Port to connect to
  106.     *
  107.     * @var integer
  108.     */
  109.     var $_port;
  110.  
  111.     /**
  112.     * To allow class debuging
  113.     * @var boolean
  114.     */
  115.     var $_debug = false;
  116.  
  117.  
  118.     /**
  119.     * The auth methods this class support
  120.     * @var array
  121.     */
  122.     //var $supportedAuthMethods=array('DIGEST-MD5', 'CRAM-MD5', 'APOP' , 'PLAIN' , 'LOGIN', 'USER');
  123.     //Disabling DIGEST-MD5 for now
  124.     var $supportedAuthMethods=array( 'CRAM-MD5', 'APOP' , 'PLAIN' , 'LOGIN', 'USER');
  125.     //var $supportedAuthMethods=array( 'CRAM-MD5', 'PLAIN' , 'LOGIN');
  126.     //var $supportedAuthMethods=array( 'PLAIN' , 'LOGIN');
  127.  
  128.  
  129.     /**
  130.     * The auth methods this class support
  131.     * @var array
  132.     */
  133.     var $supportedSASLAuthMethods=array('DIGEST-MD5', 'CRAM-MD5');
  134.  
  135.  
  136.     /**
  137.     * The capability response
  138.     * @var array
  139.     */
  140.     var $_capability;
  141.  
  142.    /*
  143.     * Constructor. Sets up the object variables, and instantiates
  144.     * the socket object.
  145.     *
  146.     */
  147.  
  148.  
  149.     function Net_POP3()
  150.     {
  151.         $this->_timestamp =  ''; // Used for APOP
  152.         $this->_maildrop  =  array();
  153.         $this->_timeout   =  3;
  154.         $this->_state     =  NET_POP3_STATE_DISCONNECTED;
  155.         $this->_socket    =& new Net_Socket();
  156.         /*
  157.         * Include the Auth_SASL package.  If the package is not available,
  158.         * we disable the authentication methods that depend upon it.
  159.         */
  160.         if ((@include_once 'Auth/SASL.php') == false) {
  161.             if($this->_debug){
  162.                 echo "AUTH_SASL NOT PRESENT!\n";
  163.             }
  164.             foreach($this->supportedSASLAuthMethods as $SASLMethod){
  165.                 $pos = array_search( $SASLMethod, $this->supportedAuthMethods );
  166.                 if($this->_debug){
  167.                     echo "DISABLING METHOD $SASLMethod\n";
  168.                 }
  169.                 unset($this->supportedAuthMethods[$pos]);
  170.             }
  171.         }
  172.  
  173.  
  174.  
  175.     }
  176.  
  177.  
  178.     /**
  179.     * Handles the errors the class can find
  180.     * on the server
  181.     *
  182.     * @access private
  183.     * @return PEAR_Error
  184.     */
  185.  
  186.     function _raiseError($msg, $code =-1)
  187.     {
  188.     include_once 'PEAR.php';
  189.     return PEAR::raiseError($msg, $code);
  190.     }
  191.  
  192.  
  193.     
  194.     /*
  195.     * Connects to the given host on the given port.
  196.     * Also looks for the timestamp in the greeting
  197.     * needed for APOP authentication
  198.     *
  199.     * @param  string $host Hostname/IP address to connect to
  200.     * @param  string $port Port to use to connect to on host
  201.     * @return bool  Success/Failure
  202.     */
  203.     function connect($host = 'localhost', $port = 110)
  204.     {
  205.         $this->_host = $host;
  206.         $this->_port = $port;
  207.  
  208.         $result = $this->_socket->connect($host, $port, false, $this->_timeout);
  209.         if ($result === true) {
  210.             $data = $this->_recvLn();
  211.  
  212.             if( $this->_checkResponse($data) ){
  213.             // if the response begins with '+OK' ...
  214. //            if (@substr(strtoupper($data), 0, 3) == '+OK') {
  215.                 // Check for string matching apop timestamp
  216.                 if (preg_match('/<.+@.+>/U', $data, $matches)) {
  217.                     $this->_timestamp = $matches[0];
  218.                 }
  219.                 $this->_maildrop = array();
  220.                 $this->_state    = NET_POP3_STATE_AUTHORISATION;
  221.  
  222.                 return true;
  223.             }
  224.         }
  225.  
  226.         $this->_socket->disconnect();
  227.         return false;
  228.     }
  229.  
  230.     /*
  231.     * Disconnect function. Sends the QUIT command
  232.     * and closes the socket.
  233.     *
  234.     * @return bool Success/Failure
  235.     */
  236.     function disconnect()
  237.     {
  238.         return $this->_cmdQuit();
  239.     }
  240.  
  241.     /*
  242.     * Performs the login procedure. If there is a timestamp
  243.     * stored, APOP will be tried first, then basic USER/PASS.
  244.     *
  245.     * @param  string $user Username to use
  246.     * @param  string $pass Password to use
  247.     * @param  mixed $apop Whether to try APOP first, if used as string you can select the auth methd to use ( $pop3->login('validlogin', 'validpass', "CRAM-MD5");
  248.     *          Valid methods are: 'DIGEST-MD5','CRAM-MD5','LOGIN','PLAIN','APOP','USER' 
  249.     * @return mixed  true on Success/ PEAR_ERROR on error
  250.     */
  251.     function login($user, $pass, $apop = true)
  252.     {
  253.         if ($this->_state == NET_POP3_STATE_AUTHORISATION) {
  254.  
  255.             if(PEAR::isError($ret= $this->_cmdAuthenticate($user , $pass , $apop ) ) ){
  256.                 return $ret;
  257.             }
  258.             if( ! PEAR::isError($ret)){
  259.                 $this->_state = NET_POP3_STATE_TRANSACTION;
  260.                 return true;
  261.             }
  262.  
  263.         }
  264.         return $this->_raiseError('Generic login error' , 1);
  265.     }
  266.  
  267.  
  268.  
  269.     /**
  270.     * Parses the response from the capability command. Stores
  271.     * the result in $this->_capability
  272.     *
  273.     * @access private
  274.     */
  275.     function _parseCapability()
  276.     {
  277.  
  278.         if(!PEAR::isError($data = $this->_sendCmd('CAPA'))){
  279.             $data = $this->_getMultiline();
  280.         }else {
  281.             // CAPA command not supported, reset data var
  282.             //  to avoid Notice errors of preg_split on an object
  283.             $data = '';
  284.         }
  285.         $data = preg_split('/\r?\n/', $data, -1, PREG_SPLIT_NO_EMPTY);
  286.  
  287.         for ($i = 0; $i < count($data); $i++) {
  288.  
  289.             $capa='';
  290.             if (preg_match('/^([a-z,\-]+)( ((.*))|$)$/i', $data[$i], $matches)) {
  291.  
  292.                 $capa=strtolower($matches[1]);
  293.                 switch ($capa) {
  294.                     case 'implementation':
  295.                         $this->_capability['implementation'] = $matches[3];
  296.                         break;
  297.                     case 'sasl':
  298.                         $this->_capability['sasl'] = preg_split('/\s+/', $matches[3]);
  299.                         break;
  300.                     default :
  301.                         $this->_capability[$capa] = $matches[2];
  302.                         break;
  303.                 }
  304.             }
  305.         }
  306.     }
  307.  
  308.  
  309.  
  310.  
  311.     /**
  312.      * Returns the name of the best authentication method that the server
  313.      * has advertised.
  314.      *
  315.      * @param string if !=null,authenticate with this method ($userMethod).
  316.      *
  317.      * @return mixed    Returns a string containing the name of the best
  318.      *                  supported authentication method or a PEAR_Error object
  319.      *                  if a failure condition is encountered.
  320.      * @access private
  321.      * @since  1.0
  322.      */
  323.     function _getBestAuthMethod($userMethod = null)
  324.     {
  325.  
  326. /*
  327.        return 'USER';
  328.        return 'APOP';
  329.        return 'DIGEST-MD5';
  330.        return 'CRAM-MD5';
  331. */
  332.  
  333.  
  334.         $this->_parseCapability();
  335.  
  336.         //unset($this->_capability['sasl']);
  337.  
  338.        if( isset($this->_capability['sasl']) ){
  339.            $serverMethods=$this->_capability['sasl'];
  340.        }else{
  341.             $serverMethods=array('USER');
  342.             // Check for timestamp before attempting APOP
  343.             if ($this->_timestamp != null)
  344.             {
  345.                 $serverMethods[] = 'APOP';
  346.             }
  347.        }
  348.  
  349.         if($userMethod !== null && $userMethod !== true ){
  350.             $methods = array();
  351.             $methods[] = $userMethod;
  352.             return $userMethod;
  353.         }else{
  354.             $methods = $this->supportedAuthMethods;
  355.         }
  356.  
  357.         if( ($methods != null) && ($serverMethods != null)){
  358.  
  359.             foreach ( $methods as $method ) {
  360.  
  361.                 if ( in_array( $method , $serverMethods ) ) {
  362.                     return $method;
  363.                 }
  364.             }
  365.             $serverMethods=implode(',' , $serverMethods );
  366.             $myMethods=implode(',' ,$this->supportedAuthMethods);
  367.             return $this->_raiseError("$method NOT supported authentication method!. This server " .
  368.                 "supports these methods: $serverMethods, but I support $myMethods");
  369.         }else{
  370.             return $this->_raiseError("This server don't support any Auth methods");
  371.         }
  372.     }
  373.  
  374.  
  375.  
  376.  
  377.  
  378.  
  379.     /* Handles the authentication using any known method
  380.      *
  381.      * @param string The userid to authenticate as.
  382.      * @param string The password to authenticate with.
  383.      * @param string The method to use ( if $usermethod == '' then the class chooses the best method (the stronger is the best ) )
  384.      *
  385.      * @return mixed  string or PEAR_Error
  386.      *
  387.      * @access private
  388.      * @since  1.0
  389.      */
  390.     function _cmdAuthenticate($uid , $pwd , $userMethod = null )
  391.     {
  392.  
  393.  
  394.         if ( PEAR::isError( $method = $this->_getBestAuthMethod($userMethod) ) ) {
  395.             return $method;
  396.         }
  397.  
  398.         switch ($method) {
  399.             case 'DIGEST-MD5':
  400.                 $result = $this->_authDigest_MD5( $uid , $pwd );
  401.                 break;
  402.             case 'CRAM-MD5':
  403.                 $result = $this->_authCRAM_MD5( $uid , $pwd );
  404.                 break;
  405.             case 'LOGIN':
  406.                 $result = $this->_authLOGIN( $uid , $pwd );
  407.                 break;
  408.             case 'PLAIN':
  409.                 $result = $this->_authPLAIN( $uid , $pwd );
  410.                 break;
  411.             case 'APOP':
  412.                 $result = $this->_cmdApop( $uid , $pwd );
  413.                 // if APOP fails fallback to USER auth
  414.                 if( PEAR::isError( $result ) ){
  415.                     //echo "APOP FAILED!!!\n";
  416.                     $result=$this->_authUSER( $uid , $pwd );
  417.                 }
  418.                 break;
  419.             case 'USER':
  420.                 $result = $this->_authUSER( $uid , $pwd );
  421.             break;
  422.  
  423.  
  424.             default :
  425.                 $result = $this->_raiseError( "$method is not a supported authentication method" );
  426.                 break;
  427.         }
  428.         return $result;
  429.     }
  430.  
  431.  
  432.  
  433.  
  434.      /* Authenticates the user using the USER-PASS method.
  435.      *
  436.      * @param string The userid to authenticate as.
  437.      * @param string The password to authenticate with.
  438.      *
  439.      * @return mixed    true on success or PEAR_Error on failure
  440.      *
  441.      * @access private
  442.      * @since  1.0
  443.      */
  444.     function _authUSER($user, $pass  )
  445.     {
  446.         if ( PEAR::isError($ret=$this->_cmdUser($user) ) ){
  447.             return $ret;
  448.         }
  449.         if ( PEAR::isError($ret=$this->_cmdPass($pass) ) ){
  450.             return $ret;
  451.         }
  452.         return true;
  453.     }
  454.  
  455.  
  456.  
  457.  
  458.  
  459.  
  460.  
  461.  
  462.      /* Authenticates the user using the PLAIN method.
  463.      *
  464.      * @param string The userid to authenticate as.
  465.      * @param string The password to authenticate with.
  466.      *
  467.      * @return array Returns an array containing the response
  468.      *
  469.      * @access private
  470.      * @since  1.0
  471.      */
  472.     function _authPLAIN($user, $pass  )
  473.     {
  474.         $cmd=sprintf('AUTH PLAIN %s', base64_encode( chr(0) . $user . chr(0) . $pass ) );
  475.  
  476.         if ( PEAR::isError( $ret = $this->_send($cmd) ) ) {
  477.             return $ret;
  478.         }
  479.         if ( PEAR::isError( $challenge = $this->_recvLn() ) ){
  480.             return $challenge;
  481.         }
  482.         if( PEAR::isError($ret=$this->_checkResponse($challenge) )){
  483.             return $ret;
  484.         }
  485.         
  486.         return true;
  487.     }
  488.  
  489.  
  490.  
  491.      /* Authenticates the user using the PLAIN method.
  492.      *
  493.      * @param string The userid to authenticate as.
  494.      * @param string The password to authenticate with.
  495.      *
  496.      * @return array Returns an array containing the response
  497.      *
  498.      * @access private
  499.      * @since  1.0
  500.      */
  501.     function _authLOGIN($user, $pass  )
  502.     {
  503.         $this->_send('AUTH LOGIN');
  504.  
  505.         if ( PEAR::isError( $challenge = $this->_recvLn() ) ) {
  506.             return $challenge;
  507.         }
  508.         if( PEAR::isError($ret=$this->_checkResponse($challenge) )){
  509.             return $ret;
  510.         }
  511.  
  512.  
  513.         if ( PEAR::isError( $ret = $this->_send(sprintf('%s', base64_encode($user))) ) ) {
  514.             return $ret;
  515.         }
  516.  
  517.         if ( PEAR::isError( $challenge = $this->_recvLn() ) ) {
  518.             return $challenge;
  519.         }
  520.         if( PEAR::isError($ret=$this->_checkResponse($challenge) )){
  521.             return $ret;
  522.         }
  523.  
  524.         if ( PEAR::isError( $ret = $this->_send(sprintf('%s', base64_encode($pass))) ) ) {
  525.             return $ret;
  526.         }
  527.  
  528.         if ( PEAR::isError( $challenge = $this->_recvLn() ) ) {
  529.             return $challenge;
  530.         }
  531.         return $this->_checkResponse($challenge);
  532.     }
  533.  
  534.  
  535.  
  536.  
  537.  
  538.      /* Authenticates the user using the CRAM-MD5 method.
  539.      *
  540.      * @param string The userid to authenticate as.
  541.      * @param string The password to authenticate with.
  542.      *
  543.      * @return array Returns an array containing the response
  544.      *
  545.      * @access private
  546.      * @since  1.0
  547.      */
  548.     function _authCRAM_MD5($uid, $pwd )
  549.     {
  550.         if ( PEAR::isError( $ret = $this->_send( 'AUTH CRAM-MD5' ) ) ) {
  551.             return $ret;
  552.         }
  553.  
  554.         if ( PEAR::isError( $challenge = $this->_recvLn() ) ) {
  555.             return $challenge;
  556.         }
  557.         if( PEAR::isError($ret=$this->_checkResponse($challenge) )){
  558.             return $ret;
  559.         }
  560.  
  561.         // remove '+ '
  562.         
  563.         $challenge=substr($challenge,2);
  564.         
  565.         $challenge = base64_decode( $challenge );
  566.  
  567.         $cram = &Auth_SASL::factory('crammd5');
  568.         $auth_str = base64_encode( $cram->getResponse( $uid , $pwd , $challenge ) );
  569.  
  570.  
  571.         if ( PEAR::isError($error = $this->_send( $auth_str ) ) ) {
  572.             return $error;
  573.         }
  574.         if ( PEAR::isError( $ret = $this->_recvLn() ) ) {
  575.             return $ret;
  576.         }
  577.         //echo "RET:$ret\n";
  578.         return $this->_checkResponse($ret);
  579.     }
  580.  
  581.  
  582.  
  583.      /* Authenticates the user using the DIGEST-MD5 method.
  584.      *
  585.      * @param string The userid to authenticate as.
  586.      * @param string The password to authenticate with.
  587.      * @param string The efective user
  588.      *
  589.      * @return array Returns an array containing the response
  590.      *
  591.      * @access private
  592.      * @since  1.0
  593.      */
  594.     function _authDigest_MD5($uid, $pwd)
  595.     {
  596.         if ( PEAR::isError( $ret = $this->_send( 'AUTH DIGEST-MD5' ) ) ) {
  597.             return $ret;
  598.         }
  599.  
  600.         if ( PEAR::isError( $challenge = $this->_recvLn() ) ) {
  601.             return $challenge;
  602.         }
  603.         if( PEAR::isError($ret=$this->_checkResponse($challenge) )){
  604.             return $ret;
  605.         }
  606.  
  607.         // remove '+ '
  608.         $challenge=substr($challenge,2);
  609.  
  610.         $challenge = base64_decode( $challenge );
  611.         $digest = &Auth_SASL::factory('digestmd5');
  612.         $auth_str = base64_encode($digest->getResponse($uid, $pwd, $challenge, "localhost", "pop3" ));
  613.  
  614.         if ( PEAR::isError($error = $this->_send( $auth_str ) ) ) {
  615.             return $error;
  616.         }
  617.  
  618.         if ( PEAR::isError( $challenge = $this->_recvLn() ) ) {
  619.             return $challenge;
  620.         }
  621.         if( PEAR::isError($ret=$this->_checkResponse($challenge) )){
  622.             return $ret;
  623.         }
  624.          /*
  625.          * We don't use the protocol's third step because POP3 doesn't allow
  626.          * subsequent authentication, so we just silently ignore it.
  627.          */
  628.  
  629.         if ( PEAR::isError( $challenge = $this->_send("\r\n") ) ) {
  630.             return $challenge ;
  631.         }
  632.         
  633.         if ( PEAR::isError( $challenge = $this->_recvLn() ) ) {
  634.             return $challenge;
  635.         }
  636.         
  637.         return $this->_checkResponse($challenge);
  638.         
  639.  
  640.     }
  641.  
  642.  
  643.  
  644.  
  645.  
  646.  
  647.  
  648.  
  649.  
  650.  
  651.     /*
  652.     * Sends the APOP command
  653.     *
  654.     * @param  $user Username to send
  655.     * @param  $pass Password to send
  656.     * @return bool Success/Failure
  657.     */
  658.     function _cmdApop($user, $pass)
  659.     {
  660.         if ($this->_state == NET_POP3_STATE_AUTHORISATION) {
  661.  
  662.             if (!empty($this->_timestamp)) {
  663.                 if(PEAR::isError($data = $this->_sendCmd('APOP ' . $user . ' ' . md5($this->_timestamp . $pass)) ) ){
  664.                     return $data;
  665.                 }
  666.                 $this->_state = NET_POP3_STATE_TRANSACTION;
  667.                 return true;
  668.             }
  669.         }
  670.         return $this->_raiseError('Not In NET_POP3_STATE_AUTHORISATION State1');
  671.     }
  672.  
  673.  
  674.  
  675.  
  676.  
  677.  
  678.  
  679.  
  680.  
  681.  
  682.  
  683.  
  684.  
  685.  
  686.  
  687.     /*
  688.     * Returns the raw headers of the specified message.
  689.     *
  690.     * @param  integer $msg_id Message number
  691.     * @return mixed   Either raw headers or false on error
  692.     */
  693.     function getRawHeaders($msg_id)
  694.     {
  695.         if ($this->_state == NET_POP3_STATE_TRANSACTION) {
  696.             return $this->_cmdTop($msg_id, 0);
  697.         }
  698.  
  699.         return false;
  700.     }
  701.  
  702.     /*
  703.     * Returns the  headers of the specified message in an
  704.     * associative array. Array keys are the header names, array
  705.     * values are the header values. In the case of multiple headers
  706.     * having the same names, eg Received:, the array value will be
  707.     * an indexed array of all the header values.
  708.     *
  709.     * @param  integer $msg_id Message number
  710.     * @return mixed   Either array of headers or false on error
  711.     */
  712.     function getParsedHeaders($msg_id)
  713.     {
  714.         if ($this->_state == NET_POP3_STATE_TRANSACTION) {
  715.  
  716.             $raw_headers = rtrim($this->getRawHeaders($msg_id));
  717.  
  718.             $raw_headers = preg_replace("/\r\n[ \t]+/", ' ', $raw_headers); // Unfold headers
  719.             $raw_headers = explode("\r\n", $raw_headers);
  720.             foreach ($raw_headers as $value) {
  721.                 $name  = substr($value, 0, $pos = strpos($value, ':'));
  722.                 $value = ltrim(substr($value, $pos + 1));
  723.                 if (isset($headers[$name]) AND is_array($headers[$name])) {
  724.                     $headers[$name][] = $value;
  725.                 } elseif (isset($headers[$name])) {
  726.                     $headers[$name] = array($headers[$name], $value);
  727.                 } else {
  728.                     $headers[$name] = $value;
  729.                 }
  730.             }
  731.  
  732.             return $headers;
  733.         }
  734.  
  735.         return false;
  736.     }
  737.  
  738.     /*
  739.     * Returns the body of the message with given message number.
  740.     *
  741.     * @param  integer $msg_id Message number
  742.     * @return mixed   Either message body or false on error
  743.     */
  744.     function getBody($msg_id)
  745.     {
  746.         if ($this->_state == NET_POP3_STATE_TRANSACTION) {
  747.             $msg = $this->_cmdRetr($msg_id);
  748.             return substr($msg, strpos($msg, "\r\n\r\n")+4);
  749.         }
  750.  
  751.         return false;
  752.     }
  753.  
  754.     /*
  755.     * Returns the entire message with given message number.
  756.     *
  757.     * @param  integer $msg_id Message number
  758.     * @return mixed   Either entire message or false on error
  759.     */
  760.     function getMsg($msg_id)
  761.     {
  762.         if ($this->_state == NET_POP3_STATE_TRANSACTION) {
  763.             return $this->_cmdRetr($msg_id);
  764.         }
  765.  
  766.         return false;
  767.     }
  768.  
  769.     /*
  770.     * Returns the size of the maildrop
  771.     *
  772.     * @return mixed Either size of maildrop or false on error
  773.     */
  774.     function getSize()
  775.     {
  776.         if ($this->_state == NET_POP3_STATE_TRANSACTION) {
  777.             if (isset($this->_maildrop['size'])) {
  778.                 return $this->_maildrop['size'];
  779.             } else {
  780.                 list(, $size) = $this->_cmdStat();
  781.                 return $size;
  782.             }
  783.         }
  784.  
  785.         return false;
  786.     }
  787.  
  788.     /*
  789.     * Returns number of messages in this maildrop
  790.     *
  791.     * @return mixed Either number of messages or false on error
  792.     */
  793.     function numMsg()
  794.     {
  795.         if ($this->_state == NET_POP3_STATE_TRANSACTION) {
  796.             if (isset($this->_maildrop['num_msg'])) {
  797.                 return $this->_maildrop['num_msg'];
  798.             } else {
  799.                 list($num_msg, ) = $this->_cmdStat();
  800.                 return $num_msg;
  801.             }
  802.         }
  803.  
  804.         return false;
  805.     }
  806.  
  807.     /*
  808.     * Marks a message for deletion. Only will be deleted if the
  809.     * disconnect() method is called.
  810.     *
  811.     * @param  integer $msg_id Message to delete
  812.     * @return bool Success/Failure
  813.     */
  814.     function deleteMsg($msg_id)
  815.     {
  816.         if ($this->_state == NET_POP3_STATE_TRANSACTION) {
  817.             return $this->_cmdDele($msg_id);
  818.         }
  819.  
  820.         return false;
  821.     }
  822.  
  823.     /*
  824.     * Combination of LIST/UIDL commands, returns an array
  825.     * of data
  826.     *
  827.     * @param  integer $msg_id Optional message number
  828.     * @return mixed Array of data or false on error
  829.     */
  830.     function getListing($msg_id = null)
  831.     {
  832.     
  833.         if ($this->_state == NET_POP3_STATE_TRANSACTION) {
  834.             if (!isset($msg_id)){
  835.             
  836.                 $list=array();
  837.                 if ($list = $this->_cmdList()) {
  838.                     if ($uidl = $this->_cmdUidl()) {
  839.                         foreach ($uidl as $i => $value) {
  840.                             $list[$i]['uidl'] = $value['uidl'];
  841.                         }
  842.                     }
  843.                     return $list;
  844.                 }else{
  845.                     return array();
  846.                 }
  847.             } else {
  848.                 if ($list = $this->_cmdList($msg_id) AND $uidl = $this->_cmdUidl($msg_id)) {
  849.                     return array_merge($list, $uidl);
  850.                 }
  851.             }
  852.         }
  853.  
  854.         return false;
  855.     }
  856.  
  857.     /*
  858.     * Sends the USER command
  859.     *
  860.     * @param  string $user Username to send
  861.     * @return bool  Success/Failure
  862.     */
  863.     function _cmdUser($user)
  864.     {
  865.         if ($this->_state == NET_POP3_STATE_AUTHORISATION) {
  866.             return $this->_sendCmd('USER ' . $user);
  867.         }
  868.         return $this->_raiseError('Not In NET_POP3_STATE_AUTHORISATION State');
  869.     }
  870.  
  871.  
  872.     /*
  873.     * Sends the PASS command
  874.     *
  875.     * @param  string $pass Password to send
  876.     * @return bool  Success/Failure
  877.     */
  878.     function _cmdPass($pass)
  879.     {
  880.         if ($this->_state == NET_POP3_STATE_AUTHORISATION) {
  881.             return $this->_sendCmd('PASS ' . $pass);
  882.         }
  883.         return $this->_raiseError('Not In NET_POP3_STATE_AUTHORISATION State');
  884.     }
  885.  
  886.  
  887.     /*
  888.     * Sends the STAT command
  889.     *
  890.     * @return mixed Indexed array of number of messages and
  891.     *               maildrop size, or false on error.
  892.     */
  893.     function _cmdStat()
  894.     {
  895.         if ($this->_state == NET_POP3_STATE_TRANSACTION) {
  896.             if(!PEAR::isError($data = $this->_sendCmd('STAT'))){
  897.                 sscanf($data, '+OK %d %d', $msg_num, $size);
  898.                 $this->_maildrop['num_msg'] = $msg_num;
  899.                 $this->_maildrop['size']    = $size;
  900.  
  901.                 return array($msg_num, $size);
  902.             }
  903.         }
  904.         return false;
  905.     }
  906.  
  907.  
  908.     /*
  909.     * Sends the LIST command
  910.     *
  911.     * @param  integer $msg_id Optional message number
  912.     * @return mixed   Indexed array of msg_id/msg size or
  913.     *                 false on error
  914.     */
  915.     function _cmdList($msg_id = null)
  916.     {
  917.         $return=array();
  918.         if ($this->_state == NET_POP3_STATE_TRANSACTION) {
  919.             if (!isset($msg_id)) {
  920.                 if(!PEAR::isError($data = $this->_sendCmd('LIST') )){
  921.                     $data = $this->_getMultiline();
  922.                     $data = explode("\r\n", $data);                    
  923.                     foreach ($data as $line) {
  924.                         if($line !=''){
  925.                             sscanf($line, '%s %s', $msg_id, $size);
  926.                             $return[] = array('msg_id' => $msg_id, 'size' => $size);
  927.                         }
  928.                     }
  929.                     return $return;
  930.                 }
  931.             } else {
  932.                 if(!PEAR::isError($data = $this->_sendCmd('LIST ' . $msg_id))){
  933.                     if($data!=''){
  934.                         sscanf($data, '+OK %d %d', $msg_id, $size);
  935.                         return array('msg_id' => $msg_id, 'size' => $size);
  936.                     }
  937.                     return array();
  938.                 }
  939.             }
  940.         }
  941.         
  942.  
  943.         return false;
  944.     }
  945.  
  946.  
  947.     /*
  948.     * Sends the RETR command
  949.     *
  950.     * @param  integer $msg_id The message number to retrieve
  951.     * @return mixed   The message or false on error
  952.     */
  953.     function _cmdRetr($msg_id)
  954.     {
  955.         if ($this->_state == NET_POP3_STATE_TRANSACTION) {
  956.             if(!PEAR::isError($data = $this->_sendCmd('RETR ' . $msg_id) )){
  957.                 $data = $this->_getMultiline();
  958.                 return $data;
  959.             }
  960.         }
  961.  
  962.         return false;
  963.     }
  964.  
  965.  
  966.     /*
  967.     * Sends the DELE command
  968.     *
  969.     * @param  integer $msg_id Message number to mark as deleted
  970.     * @return bool Success/Failure
  971.     */
  972.     function _cmdDele($msg_id)
  973.     {
  974.         if ($this->_state == NET_POP3_STATE_TRANSACTION) {
  975.             return $this->_sendCmd('DELE ' . $msg_id);
  976.         }
  977.  
  978.         return false;
  979.     }
  980.  
  981.  
  982.     /*
  983.     * Sends the NOOP command
  984.     *
  985.     * @return bool Success/Failure
  986.     */
  987.     function _cmdNoop()
  988.     {
  989.         if ($this->_state == NET_POP3_STATE_TRANSACTION) {
  990.             if(!PEAR::isError($data = $this->_sendCmd('NOOP'))){
  991.                 return true;
  992.             }
  993.         }
  994.  
  995.         return false;
  996.     }
  997.  
  998.     /*
  999.     * Sends the RSET command
  1000.     *
  1001.     * @return bool Success/Failure
  1002.     */
  1003.     function _cmdRset()
  1004.     {
  1005.         if ($this->_state == NET_POP3_STATE_TRANSACTION) {
  1006.             if(!PEAR::isError($data = $this->_sendCmd('RSET'))){
  1007.                 return true;
  1008.             }
  1009.         }
  1010.  
  1011.         return false;
  1012.     }
  1013.  
  1014.     /*
  1015.     * Sends the QUIT command
  1016.     *
  1017.     * @return bool Success/Failure
  1018.     */
  1019.     function _cmdQuit()
  1020.     {
  1021.         $data = $this->_sendCmd('QUIT');
  1022.         $this->_state = NET_POP3_STATE_DISCONNECTED;
  1023.         $this->_socket->disconnect();
  1024.  
  1025.         return (bool)$data;
  1026.     }
  1027.  
  1028.  
  1029.     /*
  1030.     * Sends the TOP command
  1031.     *
  1032.     * @param  integer  $msg_id    Message number
  1033.     * @param  integer  $num_lines Number of lines to retrieve
  1034.     * @return mixed Message data or false on error
  1035.     */
  1036.     function _cmdTop($msg_id, $num_lines)
  1037.     {
  1038.         if ($this->_state == NET_POP3_STATE_TRANSACTION) {
  1039.  
  1040.             if(!PEAR::isError($data = $this->_sendCmd('TOP ' . $msg_id . ' ' . $num_lines))){
  1041.                 return $this->_getMultiline();
  1042.             }
  1043.         }
  1044.  
  1045.         return false;
  1046.     }
  1047.  
  1048.     /*
  1049.     * Sends the UIDL command
  1050.     *
  1051.     * @param  integer $msg_id Message number
  1052.     * @return mixed indexed array of msg_id/uidl or false on error
  1053.     */
  1054.     function _cmdUidl($msg_id = null)
  1055.     {
  1056.         if ($this->_state == NET_POP3_STATE_TRANSACTION) {
  1057.  
  1058.             if (!isset($msg_id)) {
  1059.                 if(!PEAR::isError($data = $this->_sendCmd('UIDL') )){
  1060.                     $data = $this->_getMultiline();
  1061.                     $data = explode("\r\n", $data);
  1062.                     foreach ($data as $line) {
  1063.                         sscanf($line, '%d %s', $msg_id, $uidl);
  1064.                         $return[] = array('msg_id' => $msg_id, 'uidl' => $uidl);
  1065.                     }
  1066.  
  1067.                     return $return;
  1068.                 }
  1069.             } else {
  1070.  
  1071.                 $data = $this->_sendCmd('UIDL ' . $msg_id);
  1072.                 sscanf($data, '+OK %d %s', $msg_id, $uidl);
  1073.                 return array('msg_id' => $msg_id, 'uidl' => $uidl);
  1074.             }
  1075.         }
  1076.  
  1077.         return false;
  1078.     }
  1079.  
  1080.  
  1081.  
  1082.  
  1083.  
  1084.  
  1085.  
  1086.  
  1087.  
  1088.     /*
  1089.     * Sends a command, checks the reponse, and
  1090.     * if good returns the reponse, other wise
  1091.     * returns false.
  1092.     *
  1093.     * @param  string $cmd  Command to send (\r\n will be appended)
  1094.     * @return mixed First line of response if successful, otherwise false
  1095.     */
  1096.     function _sendCmd($cmd)
  1097.     {
  1098.         if (PEAR::isError($result = $this->_send($cmd) )){
  1099.             return $result ;
  1100.         }
  1101.  
  1102.         if (PEAR::isError($data = $this->_recvLn() )){
  1103.             return $data;
  1104.         }
  1105.         
  1106.         if ( strtoupper(substr($data, 0, 3)) == '+OK') {
  1107.             return $data;
  1108.         }
  1109.         
  1110.         
  1111.         return $this->_raiseError($data);
  1112.     }
  1113.  
  1114.     /*
  1115.     * Reads a multiline reponse and returns the data
  1116.     *
  1117.     * @return string The reponse.
  1118.     */
  1119.     function _getMultiline()
  1120.     {
  1121.         $data = '';
  1122.         while(!PEAR::isError($tmp = $this->_recvLn() ) ) {
  1123.             if($tmp == '.'){
  1124.                 return substr($data, 0, -2);
  1125.             }
  1126.             if (substr($tmp, 0, 2) == '..') {
  1127.                 $tmp = substr($tmp, 1);
  1128.             }
  1129.             $data .= $tmp . "\r\n";
  1130.         }
  1131.         return substr($data, 0, -2);
  1132.     }
  1133.  
  1134.  
  1135.    /**
  1136.     * Sets the bebug state
  1137.     *
  1138.     * @param  bool $debug 
  1139.     * @access public
  1140.     * @return void
  1141.     */
  1142.     function setDebug($debug=true)
  1143.     {
  1144.         $this->_debug=$debug;
  1145.     }
  1146.  
  1147.  
  1148.  
  1149.  
  1150.  
  1151.    /**
  1152.      * Send the given string of data to the server.
  1153.      *
  1154.      * @param   string  $data       The string of data to send.
  1155.      *
  1156.      * @return  mixed   True on success or a PEAR_Error object on failure.
  1157.      *
  1158.      * @access  private
  1159.      * @since   1.0
  1160.      */
  1161.     function _send($data)
  1162.     {
  1163.         if ($this->_debug) {
  1164.             echo "C: $data\n";
  1165.         }
  1166.  
  1167.         if (PEAR::isError($error = $this->_socket->writeLine($data))) {
  1168.             return $this->_raiseError('Failed to write to socket: ' . $error->getMessage());
  1169.         }
  1170.         return true;
  1171.     }
  1172.  
  1173.  
  1174.  
  1175.      /**
  1176.      * Receive the given string of data from the server.
  1177.      *
  1178.      * @return  mixed   a line of response on success or a PEAR_Error object on failure.
  1179.      *
  1180.      * @access  private
  1181.      * @since  1.0
  1182.      */
  1183.     function _recvLn()
  1184.     {
  1185.         if (PEAR::isError( $lastline = $this->_socket->readLine( 8192 ) ) ) {
  1186.             return $this->_raiseError('Failed to write to socket: ' . $this->lastline->getMessage() );
  1187.         }
  1188.         if($this->_debug){
  1189.             // S: means this data was sent by  the POP3 Server
  1190.             echo "S:$lastline\n" ;
  1191.         }
  1192.         return $lastline;
  1193.     }
  1194.  
  1195.      /**
  1196.      * Checks de server Response
  1197.      *
  1198.      * @param  string $response the response
  1199.      * @return  mixed   true on success or a PEAR_Error object on failure.
  1200.      *
  1201.      * @access  private
  1202.      * @since  1.3.3
  1203.      */
  1204.  
  1205.     function _checkResponse($response)
  1206.     {
  1207.         if (@substr(strtoupper($response), 0, 3) == '+OK') {
  1208.             return true;
  1209.         }else{
  1210.             if (@substr(strtoupper($response), 0, 4) == '-ERR') {
  1211.                 return $this->_raiseError($response);
  1212.             }else{
  1213.                 if (@substr(strtoupper($response), 0, 2) == '+ ') {
  1214.                     return true;
  1215.                 }
  1216.             }
  1217.     
  1218.         }
  1219.         return $this->_raiseError("Unknown Response ($response)");
  1220.     }
  1221.     
  1222.  
  1223.  
  1224. }
  1225.  
  1226. ?>
  1227.